#ifndef SYMBOLSTABLE_H_
#define SYMBOLSTABLE_H_

#include "Defines.h"

namespace LALG
{

enum Category { ALL, PROGRAM, VARIABLE, PARAMETER, PROCEDURE, CONSTANT };

/*-----------------------------------------------------------------------------
 *  Variável, constante e parâmetro
 *-----------------------------------------------------------------------------*/
struct Variable
{
    int type;
    union
    {
        int ivalue;
        float fvalue;
    };
};

/*-----------------------------------------------------------------------------
 *  Procedimento
 *-----------------------------------------------------------------------------*/
struct Procedure
{
    list<int>* params;
};

/*-----------------------------------------------------------------------------
 *  Valor de um símbolo
 *-----------------------------------------------------------------------------*/
union SymbolValue
{
    Variable var;     // Variável, constante ou parâmetro
    Procedure proc;   // Procedimento
};

/*
 * =====================================================================================
 *       Struct:  Symbol
 *  Description:  Classe que representa um símbolo de programa
 * =====================================================================================
 */
struct Symbol
{
    string name;        // Cadeia
    Category category;  // Categoria
    SymbolValue value;  // Valor
};

/*-----------------------------------------------------------------------------
 *  Ponteiro para Symbol
 *-----------------------------------------------------------------------------*/
typedef shared_ptr<Symbol> SymbolPtr;
typedef unordered_map<string, SymbolPtr> SymTable;

/*
 * =====================================================================================
 *        Class:  SymbolsTable
 *  Description:  Classe que representa a tabela de símbolos do programa
 * =====================================================================================
 */
class SymbolsTable
{

public:

    /*-----------------------------------------------------------------------------
     *  Constrói uma tabela de símbolos
     *-----------------------------------------------------------------------------*/
    SymbolsTable();

    /*-----------------------------------------------------------------------------
     *  Destrói a tabela de símbolos
     *-----------------------------------------------------------------------------*/
    ~SymbolsTable();

    /*-----------------------------------------------------------------------------
     *  Verifica se existe uma declaração
     *-----------------------------------------------------------------------------*/
    bool exists(const string &name, Category category = ALL);

    /*-----------------------------------------------------------------------------
     *  Adiciona um símbolo
     *-----------------------------------------------------------------------------*/
    bool addSymbol(const string &name, Category category, SymbolValue &value);

    /*-----------------------------------------------------------------------------
     *  Adiciona um programa
     *-----------------------------------------------------------------------------*/
    void addProgram(const string &name);

    /*-----------------------------------------------------------------------------
     *  Adiciona uma constante
     *-----------------------------------------------------------------------------*/
    bool addConstant(const string &name, Variable &var);

    /*-----------------------------------------------------------------------------
     *  Adiciona uma variável
     *-----------------------------------------------------------------------------*/
    bool addVariable(const string &name, Variable &var);

    /*-----------------------------------------------------------------------------
     *  Adiciona um procedimento
     *-----------------------------------------------------------------------------*/
    bool addProcedure(const string &name, Procedure &proc);

    /*-----------------------------------------------------------------------------
     *  Adiciona um parâmetro
     *-----------------------------------------------------------------------------*/
    bool addParameter(const string &name, Variable &var);

    /*-----------------------------------------------------------------------------
     *  Adiciona um novo nível
     *-----------------------------------------------------------------------------*/
    void pushLevel();

    /*-----------------------------------------------------------------------------
     *  Remove o nível mais ao topo
     *-----------------------------------------------------------------------------*/
    void popLevel();

    /*-----------------------------------------------------------------------------
     *  Retorna o valor de um símbolo
     *-----------------------------------------------------------------------------*/
    Symbol* getSymbol(const string &name);

    /*-----------------------------------------------------------------------------
     *  Retorna a categoria
     *-----------------------------------------------------------------------------*/
    Category getCategory(const string &name);

    /*-----------------------------------------------------------------------------
     *  Retorna o valor de uma variável
     *-----------------------------------------------------------------------------*/
    Variable* getVariable(const string &name);

    /*-----------------------------------------------------------------------------
     *  Retorna o valor de um procedimento
     *-----------------------------------------------------------------------------*/
    Procedure* getProcedure(const string &name);

private:

    /*-----------------------------------------------------------------------------
     *  Deleta uma tabela de símbolos
     *-----------------------------------------------------------------------------*/
    void deleteSymbols(SymTable* symbols);

    /*-----------------------------------------------------------------------------
     *  Níveis
     *-----------------------------------------------------------------------------*/
    list<SymTable*> level;

    /*-----------------------------------------------------------------------------
     *  Tabela de símbolos
     *-----------------------------------------------------------------------------*/
    SymTable* root;

};

}

#endif /* SYMBOLSTABLE_H_ */
